home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / DTS.Draw / Window2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-22  |  38.4 KB  |  1,261 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window2.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* This file contains the code for the document procedure pointers for the main DTS.Draw
  12. ** document.  DTS.Draw currently only supports one type of documents, type 'DDOC'. */
  13.  
  14. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  15.  
  16.  
  17.  
  18. /*****************************************************************************/
  19.  
  20.  
  21.  
  22. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  23. #include "App.Common.h"        /* Get the stuff in common with rez.            */
  24. #include "App.protos.h"        /* Get the prototypes for the application.        */
  25.  
  26. #ifndef __AppMenu__
  27. #include "App.Menu.h"
  28. #endif
  29.  
  30. #ifndef __ERRORS__
  31. #include <Errors.h>
  32. #endif
  33.  
  34. #ifndef __FONTS__
  35. #include <Fonts.h>
  36. #endif
  37.  
  38. #ifndef __RESOURCES__
  39. #include <Resources.h>
  40. #endif
  41.  
  42. #ifndef __TOOLUTILS__
  43. #include <ToolUtils.h>
  44. #endif
  45.  
  46. #ifndef __TREEOBJ2__
  47. #include "TreeObj2.h"
  48. #endif
  49.  
  50. #ifndef __UTILITIES__
  51. #include "Utilities.h"
  52. #endif
  53.  
  54.  
  55.  
  56. /*****************************************************************************/
  57.  
  58.  
  59.  
  60. typedef struct {
  61.     TreeObjHndl    root;
  62.     short        cnum;
  63. } LayerDrawInfo;
  64.  
  65. Boolean            gNoDefaultDocument = false;
  66.                     /* Set to true if app should boot with no default document. */
  67.                     /* This tells DTS.Lib..framework what you want. */
  68.  
  69. short            gwAppWindow    = kwAppWindow;    /* Main window attributes. */
  70. short            gMinVersion = kMinVersion;    /* Minimum document version app can support. */
  71. short            gMaxVersion = kMaxVersion;    /* Maximum document version app can support. */
  72.                                             /* More informing DTS.Lib..framework. */
  73.  
  74. extern short    gPrintPage;                    /* Non-zero means we are printing. */
  75.                                             /* DTS.Lib..framework global. */
  76.  
  77. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  78. extern CursPtr        gCursorPtr;                /* to know about these things. */
  79.                                             /* Above are DTS.Lib..framework globals. */
  80.  
  81. extern short    gTool;
  82. extern Boolean    gKeepTool;
  83.  
  84.  
  85.  
  86. static void        AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click);
  87. static void        SlideSelection(FileRecHndl frHndl, ClickInfo *click);
  88. static OSErr    WindowLayerProc(LayerObj theLayer, short message);
  89. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  90. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  91.  
  92.  
  93.  
  94. /*****************************************************************************/
  95. /*****************************************************************************/
  96.  
  97.  
  98.  
  99. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  100.  
  101. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  102. ** You are passed an empty region.  You are supposed to add any custom frame
  103. ** parts that this document uses.  Typically there are no frame portions, as
  104. ** they are accounted for in other ways.  The scrollbars and grow icon will
  105. ** automatically be contributed to the calculation of the frame region.
  106. ** If you use sidebars, these are also added in automatically.  This is only
  107. ** used if the frame region is more complicated than can automatically be
  108. ** handled.  So, almost always, you will simply leave the region empty. */
  109.  
  110. #pragma segment TheDoc
  111. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  112. {
  113. #pragma unused (frHndl, window, rgn)
  114. }
  115.  
  116.  
  117.  
  118. /*****************************************************************************/
  119.  
  120.  
  121.  
  122. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  123.  
  124. /* This is called when a mouse-down event occurs in the content of a window.
  125. ** Other applications might want to call FindControl, TEClick, etc., to
  126. ** further process the click. */
  127.  
  128. #pragma segment DrawDoc
  129. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  130. {
  131. #pragma unused (firstClick)
  132.  
  133.     FileRecHndl        frHndl;
  134.     ControlHandle    ctl;
  135.     short            dblClick, shiftMod;
  136.     Point            contOrg;
  137.     Boolean            selected;
  138.     TreeObjHndl        hitObj, root;
  139.     ClickInfo        click;
  140.  
  141.     SetPort(window);
  142.  
  143.     if (IsCtlEvent(window, event, &ctl, &dblClick)) return;
  144.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  145.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  146.         ** controls in the content besides the document scrollbars. */
  147.  
  148.     frHndl = (FileRecHndl)GetWRefCon(window);
  149.     if ((*frHndl)->fileState.readOnly) return;
  150.         /* Don't allow changes if read-only. */
  151.  
  152.     /* If none of the above resolved why we are here, then the click was in the
  153.     ** actual drawing area of the content, and we have some serious work to do. */
  154.  
  155.     GetContentOrigin(window, &contOrg);
  156.     SetOrigin(contOrg.h, contOrg.v);
  157.         /* This sets the origin of the document area of the content to the position
  158.         ** indicated by the document scrollbars. */
  159.  
  160.     click.localEvent = *event;
  161.     GlobalToLocal(&click.localEvent.where);
  162.         /* Who wants to work in global coordinates, anyway... */
  163.  
  164.     if (!gTool) {        /* If no specific tool (arrow tool)... */
  165.  
  166.         click.message = HITTESTGRABBER;
  167.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  168.             /* See if the user clicked on a grabber for a selected object. */
  169.  
  170.         if (!hitObj) {        /* If user didn't click on a grabber, see if they hit an object. */
  171.             click.message = HITTESTOBJ;
  172.             hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  173.         }
  174.  
  175.         shiftMod = click.localEvent.modifiers & shiftKey;
  176.             /* Find out if the user was holding down the shift key. */
  177.  
  178.         if (click.message == HITTESTGRABBER)
  179.             shiftMod = 0;
  180.                 /* Pretent that the shift key isn't held down if they are on a grabber.  If
  181.                 ** they are on a grabber, you want to do a constrain with the shift key. */
  182.  
  183.         if (hitObj) {        /* User hit either a grabber or the object itself. */
  184.  
  185.             selected = mDerefCommon(hitObj)->selected;
  186.                 /* See if the object hit, independent of type, was selected. */
  187.  
  188.             if ((!selected) && (!shiftMod))        /* The object isn't selected, and */
  189.                 DoTreeSelect(root, SELECTOFF);    /* the shift key isn't held down, */
  190.                                                 /* so deselect all other objects. */
  191.  
  192.             click.message = CLICKSELECT;
  193.             if ((!selected) || (shiftMod))
  194.                 DoTreeObjMethodClipped(hitObj, CLICKMESSAGE, (long)&click);
  195.                     /* If the object isn't selected, or if the shift key is down, change
  196.                     ** the select state of the object that was clicked on. */
  197.  
  198.             click.localEvent.modifiers ^= shiftMod;
  199.                 /* Turn off the shift modifier, which makes the rest of the
  200.                 ** handling of the object easier. */
  201.  
  202.             if (mDerefCommon(hitObj)->selected) {    /* If object selected, do something with it. */
  203.                 if (click.grabber == -1)
  204.                     SlideSelection(frHndl, &click);
  205.                 else
  206.                     AddOrSizeObj(frHndl, hitObj, &click);
  207.             }            /* The click could have been used to shift-click deselect an object,
  208.                         ** so only do something with the object if it is selected.  We  either
  209.                         ** resize the object (if a grabber was clicked on) or slide the selected
  210.                         ** objects (if an object was clicked on). */
  211.  
  212.         }
  213.         else if (!shiftMod)                    /* No object was clicked on, */
  214.             DoTreeSelect(root, SELECTOFF);    /* so if not a shift-click,  */
  215.                                             /* deselect everything.      */
  216.     }
  217.     else {
  218.         click.message = HITTESTGRABBER;
  219.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  220.             /* See if the user clicked on a grabber for a selected object. */
  221.         AddOrSizeObj(frHndl, hitObj, &click);
  222.             /* No object clicked on, so add an object. */
  223.     }
  224.  
  225.     DrawFrame(frHndl, window);
  226.         /* The tool palette may have changed, so show the possible change of status. */
  227. }
  228.  
  229.  
  230.  
  231. /*****************************************************************************/
  232.  
  233.  
  234.  
  235. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  236.  
  237. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  238. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  239. **
  240. ** 1) If you handle the key, return(true).  This completes the key handling.
  241. ** 2) If you don't handle the key, you return false.  However, there are two
  242. **    situations for not handling the key:
  243. **      a) You want someone else to.
  244. **      b) You want nobody else to look at the key.
  245. **    This is what the boolean passThrough is for.  If you wish the next window
  246. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  247. **    is already initialized to false, which is the common case, so you only have
  248. **    to worry about setting it true.
  249. **
  250. ** If you have a window that never processes keys and always passes them through,
  251. ** just set the contentKeyProc to nil.  This will indicate to the application
  252. ** framework that all keys should be passed through this window.  DTS.Draw has
  253. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  254. ** to document windows. */
  255.  
  256. #pragma segment DrawDoc
  257. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  258. {
  259. #pragma unused (passThrough)
  260.  
  261.     char        key;
  262.     FileRecHndl    frHndl;
  263.  
  264.     key = event->message & charCodeMask;
  265.     if (key != 8) return(false);
  266.         /* The only key we handle is the delete key.  leave for all the rest. */
  267.  
  268.     SetPort(window);
  269.  
  270.     frHndl = (FileRecHndl)GetWRefCon(window);
  271.     if ((*frHndl)->fileState.readOnly) return(false);
  272.         /* Don't allow changes if read-only. */
  273.  
  274.     DoDelete(frHndl);
  275.  
  276.     return(true);
  277. }
  278.  
  279.  
  280.  
  281. /*****************************************************************************/
  282.  
  283.  
  284.  
  285. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  286.  
  287. /* Draw application specific content (Called by DoDrawFrame).
  288. **
  289. ** If your application has any custom frame areas, or if it uses sidebars,
  290. ** this is the function that you would put the frame drawing code.  The
  291. ** document scrollbars and grow icon drawing is handled by DTS.LIB..framework.
  292. ** Just do the sidebar and custom areas here. */
  293.  
  294. #pragma segment TheDoc
  295. void    DrawFrame(FileRecHndl frHndl, WindowPtr window)
  296. {
  297. #pragma unused (frHndl, window)
  298. }
  299.  
  300.  
  301.  
  302. /*****************************************************************************/
  303.  
  304.  
  305.  
  306. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  307.  
  308. /* Frees up any application-specific memory in the document.  This is called by
  309. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  310. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  311. **
  312. ** The document may have a bunch of handles off the main handle of the document.
  313. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  314. ** the ram for the main handle of the document, so release everything else
  315. ** here, or you will have a memory leak.
  316. **
  317. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  318. ** hierarchical document (see TreeObj package). */
  319.  
  320. #pragma segment TheDoc
  321. OSErr    FreeDocument(FileRecHndl frHndl)
  322. {
  323.     return(DefaultFreeDocument(frHndl));
  324. }
  325.  
  326.  
  327.  
  328. /*****************************************************************************/
  329.  
  330.  
  331.  
  332. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  333.  
  334. /* Any additional window disposal tasks can be handled here. */
  335.  
  336. #pragma segment TheDoc
  337. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  338. {
  339. #pragma unused (frHndl, window)
  340.  
  341.     return(noErr);
  342. }
  343.  
  344.  
  345.  
  346. /*****************************************************************************/
  347.  
  348.  
  349.  
  350. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  351.  
  352. /* Image the document into the current port.
  353. **
  354. ** The only thing tricky about this function is that it needs to key off of
  355. ** the global variable gPrintPage.  gPrintPage is the current page that is
  356. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  357. **
  358. ** For when printing:
  359. **
  360. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  361. ** the page there are no more pages, you should set gPrintPage to 0.  This
  362. ** indicates to the print loop that the end of the document has been reached.
  363. ** Even if the user indicated in the job dialog to print more pages, setting
  364. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  365. ** because the print loop can't know when printing is done.  The imaging procedure
  366. ** is the logical one to state when everything has been imaged. */
  367.  
  368. #pragma segment DrawDoc
  369. OSErr    ImageDocument(FileRecHndl frHndl)
  370. {
  371.     LayerDrawInfo    drawInfo;
  372.     LayerObj        windowLayer, backLayer;
  373.  
  374.     drawInfo.root = (*frHndl)->d.doc.root;
  375.         /* If there isn't a background layer, then all objects are drawn into
  376.         ** the work layer, and therefore we don't need to fill in the cnum field
  377.         ** of the drawInfo.  The WorkLayerProc will notice that there isn't a
  378.         ** layer behind it and will automatically do the right thing. */
  379.  
  380.     if (gPrintPage) {
  381.         DoTreeDraw(drawInfo.root, DRAWOBJ);        /* Draw the page. */
  382.         gPrintPage = 0;                            /* We only support one page in this sample. */
  383.         return(noErr);
  384.     }
  385.  
  386.     NewLayer(&windowLayer, nil, WindowLayerProc, (*frHndl)->fileState.window, 0, (long)&drawInfo);
  387.         /* Create a layer object for the window. */
  388.  
  389.     NewLayer(&backLayer, windowLayer, WorkLayerProc, nil, 0, (long)&drawInfo);
  390.         /* Create a background layer for drawing of all of the objects. */
  391.  
  392.     InvalLayer(windowLayer, GetEffectiveDstRect(windowLayer), false);
  393.         /* We want to draw the entire contents. */
  394.  
  395.     UpdateLayer(windowLayer);
  396.         /* Update what's invalid, which is everything.  All the objects are drawn into
  397.         ** the background layer, and then the background layer is transferred into the
  398.         ** window. */
  399.  
  400.     DisposeThisAndBelowLayers(windowLayer);
  401.         /* Clean up what we created. */
  402.  
  403.     return(noErr);
  404. }
  405.  
  406.  
  407.  
  408. /*****************************************************************************/
  409.  
  410.  
  411.  
  412. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  413.  
  414. /* This function does the remaining window initialization.
  415. **
  416. ** There may be additional content initialization for the window.  At this point,
  417. ** you have a window, but it is currently invisible.  If you return noErr, then
  418. ** the window will be set to the state indicated for that window.  Why this function?
  419. ** You may wish to add controls to the content of the window.  You may have a
  420. ** TextEdit record in the content.  All of these sort of things can't be created
  421. ** until there is a window to contain them.  First a document is read in, and then
  422. ** if the document creation succeeds, a window is created for that document.
  423. ** At this point we have a document, and we are on our way to having a window.
  424. ** All that remains is any additional content initialization.  Do it, return
  425. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  426. ** and the incomplete window will be disposed of. */
  427.  
  428. #pragma segment TheDoc
  429. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  430. {
  431. #pragma unused (frHndl, window)
  432.  
  433.     /* See DTS.Chat for an example of what you might do here. */
  434.  
  435.     return(noErr);
  436. }
  437.  
  438.  
  439.  
  440. /*****************************************************************************/
  441.  
  442.  
  443.  
  444. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  445.  
  446. /* The code below assumes that you are using the hierarchical document package.
  447. ** If you are, the entire hierarchical document is read in with just the two
  448. ** calls.  The only custom thing we do here is to make sure that the numSelected field
  449. ** is set to zero so that documents load with no selected objects. */
  450.  
  451. #pragma segment DrawDoc
  452. OSErr    ReadDocument(FileRecHndl frHndl)
  453. {
  454.     OSErr        err;
  455.     TreeObjHndl    root;
  456.  
  457.     err = DefaultReadDocument(frHndl);
  458.     root = (*frHndl)->d.doc.root;
  459.     mDerefRoot(root)->numSelected = 0;        /* User may have saved when objects were selected. */
  460.     if (!err)
  461.         DefaultReadDocumentFixup(frHndl);
  462.  
  463.     return(err);
  464. }
  465.  
  466.  
  467.  
  468. /*****************************************************************************/
  469.  
  470.  
  471.  
  472. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  473.  
  474. /* Resize application specific content (Called by ResizeWindow).
  475. **
  476. ** This gets called when a user does a zoom or window resizing operation.
  477. ** It is possible that things in the content need to be resized in conjunction
  478. ** with the resizing of the window. */
  479.  
  480. #pragma segment TheDoc
  481. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  482. {
  483. #pragma unused (window, oldh, oldv)
  484.  
  485.     /* See DTS.Chat for a sample usage of this function. */
  486. }
  487.  
  488.  
  489.  
  490. /*****************************************************************************/
  491.  
  492.  
  493.  
  494. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  495.  
  496. /* Scroll application specific frame (Called by DoScrollFrame).
  497. **
  498. ** Some applications may need to scroll the "frame" of the document along
  499. ** with the document contents.  This is common for applications with rulers,
  500. ** or other similar sidebar items. */
  501.  
  502. #pragma segment TheDoc
  503. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  504. {
  505. #pragma unused (frHndl, window, dh, dv)
  506. }
  507.  
  508.  
  509.  
  510. /*****************************************************************************/
  511.  
  512.  
  513.  
  514. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  515.  
  516. /* This gets called just prior to and just after an undo/redo operation is done. */
  517.  
  518. #pragma segment DrawDoc
  519. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  520. {
  521.     WindowPtr    window;
  522.  
  523.     window = (*frHndl)->fileState.window;
  524.  
  525.     if (!afterUndo)            /* Before an undo operation we deselect everything. */
  526.         DoTreeSelect((*frHndl)->d.doc.root, SELECTOFF);
  527.             /* Only what was undone should end up selected. */
  528.  
  529.     if (afterUndo) {
  530.         SetContentOrigin(window, contOrg.h, contOrg.v);
  531.             /* Undo may have a different document origin than where the user was.
  532.             ** Scroll the document back to where the edit we are undoing took place. */
  533.  
  534.         BeginContent(window);            /* Redraw the document to display */
  535.         DoImageDocument(frHndl);        /* the changes due to undo. */
  536.         EndContent(window);
  537.     }
  538.  
  539.     DoSetCursor(nil);
  540. }
  541.  
  542.  
  543.  
  544. /*****************************************************************************/
  545.  
  546.  
  547.  
  548. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  549.  
  550. /* This function is where you adjust the cursor to reflect the location in the
  551. ** document or window.  You have the additional input of gCursorRgn to deal
  552. ** with.  The way that the cursor handling works is as follows:
  553. ** 1) The application calls DoWindowCursor().
  554. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  555. **    It looks at the document's windowCursorProc and checks to see if the document
  556. **    has one.  If the document doesn't have one, then it assumes that that window
  557. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  558. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  559. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  560. **    is set to an arrow (for the non-document area of the screen).
  561. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  562. **    job is as follows:
  563. **    a) If the cursor is over a position that is determined by the window, then
  564. **       the proc removes other areas from gCursorRgn.  Note that it should not
  565. **       simply set the area to what it "thinks" is the correct area.  This window
  566. **       may not be the front-most.  Other windows will have already been subtracted
  567. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  568. **       and should be passed to WaitNextEvent calls in the application (already the case
  569. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  570. **       You should also return true, as the cursor has been determined.
  571. **    b) If the cursor is not over a position for this window, then you should
  572. **       return.  You will either pass back true or false.  If you don't wish
  573. **       windows behind this window to have a shot at cursor determination, then
  574. **       return true.  This states that the cursor is "determined".  It is, in the
  575. **       sense that no further determination will occur.  If you return false, then
  576. **       other windows get a shot at determining the cursor.
  577. **
  578. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  579. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  580. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  581. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  582. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  583. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  584. ** to the return result of DoSetResCursor(), and you will be set. */
  585.  
  586. #pragma segment DrawDoc
  587. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  588. {
  589. #pragma unused (frHndl)
  590.  
  591.     RgnHandle    frameRgn, contRgn;
  592.  
  593.     if (gTool) {
  594.         frameRgn = DoCalcFrameRgn(window);
  595.         contRgn  = NewRgn();
  596.         DiffRgn(((WindowPeek)window)->contRgn, frameRgn, contRgn);
  597.         DisposeRgn(frameRgn);
  598.         if (PtInRgn(globalPt, contRgn)) {
  599.             gCursorPtr = DoSetResCursor(addObjCursor);
  600.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  601.             DisposeRgn(contRgn);
  602.             return(true);
  603.         }
  604.         DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  605.         SectRgn(gCursorRgn, contRgn, gCursorRgn);
  606.         DisposeRgn(contRgn);
  607.     }
  608.  
  609.     SetCursor(gCursorPtr = &qd.arrow);
  610.     return(true);
  611. }
  612.  
  613.  
  614.  
  615. /*****************************************************************************/
  616.  
  617.  
  618.  
  619. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  620.  
  621. /* After the DTS.Lib..framework disposes of a window, it calls here.  This is
  622. ** to give the application a chance to do any additional tasks related to
  623. ** a window closing. */
  624.  
  625. #pragma segment DrawDoc
  626. void    WindowGoneFixup(WindowPtr window)
  627. {
  628. #pragma unused (window)
  629. }
  630.  
  631.  
  632.  
  633. /*****************************************************************************/
  634.  
  635.  
  636.  
  637. #pragma segment DrawDoc
  638. OSErr    WriteDocument(FileRecHndl frHndl)
  639. {
  640.     return(DefaultWriteDocument(frHndl));
  641. }
  642.  
  643.  
  644.  
  645. /*****************************************************************************/
  646. /*****************************************************************************/
  647. /*****************************************************************************/
  648.  
  649.  
  650.  
  651. #pragma segment DrawDoc
  652. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  653. {
  654.     OSErr        err;
  655.     TreeObjHndl    root;
  656.  
  657.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  658.         /* Create a document and root object to copy the file data into. */
  659.  
  660.     if (!err)
  661.         err = CopyChildren((*oldFrHndl)->d.doc.root, (**newFrHndl)->d.doc.root);
  662.             /* Copy the hierarchical data into the new file. */
  663.  
  664.     if (!err) {
  665.         root = (**newFrHndl)->d.doc.root;
  666.         DoFTreeMethod(root, UNDOMESSAGE, UNDOFROMDOC);
  667.         mDerefRoot(root)->numSelected = 0;
  668.     }        /* We have to be careful to use methods that don't try to draw
  669.             ** anything, as we don't have a window to draw into yet. */
  670.  
  671.     return(err);
  672. }
  673.  
  674.  
  675.  
  676. /*****************************************************************************/
  677.  
  678.  
  679.  
  680. /* This is called when a key event occurs and it is determined that it isn't
  681. ** a menu key. */
  682.  
  683. #pragma segment DrawDoc
  684. void    DoDelete(FileRecHndl frHndl)
  685. {
  686.     WindowPtr    window;
  687.     TreeObjHndl    root;
  688.     short        cnum;
  689.     Boolean        didDelete;
  690.  
  691.     root = (*frHndl)->d.doc.root;
  692.         /* All of the selected objects are children of the root, so all we have to do
  693.         ** is walk the children of the root looking for selected objects.  If the
  694.         ** document has grouping objects, which have sub-children, only the group
  695.         ** object is selected. */
  696.  
  697.     for (didDelete = false, cnum = (*root)->numChildren; cnum;) {
  698.         if (mDerefCommon(GetChildHndl(root, --cnum))->selected) {
  699.             if (!didDelete)
  700.                 NewDocumentUndo(frHndl);
  701.                     /* Make sure this delete event is posted in a separate undo from
  702.                     ** a possible previous delete event. */
  703.             DisposeChild(DELETE_EDIT, root, cnum);
  704.                 /* Post the delete of the child into the undo hierarchy. */
  705.             didDelete = true;
  706.                 /* Yes, we did change the document. */
  707.         }
  708.     }
  709.  
  710.     if (didDelete) {                            /* If something got deleted... */
  711.         window = (*frHndl)->fileState.window;
  712.         SetWindowDirty(window);                    /* Flag the document as dirty. */
  713.         BeginContent(window);                    /* Redraw the contents. */
  714.         DoImageDocument(frHndl);
  715.         EndContent(window);
  716.     }
  717. }
  718.  
  719.  
  720.  
  721. /*****************************************************************************/
  722.  
  723.  
  724.  
  725. #pragma segment DrawDoc
  726. void    DoArrange(FileRecHndl frHndl, short menuItem)
  727. {
  728.     TreeObjHndl    root;
  729.     short        cnum, cdir, cend;
  730.     WindowPtr    window;
  731.  
  732.     if (menuItem >= iGroup) {
  733.         switch (menuItem) {
  734.             case iGroup:
  735.                 DoGroup(frHndl);
  736.                 break;
  737.             case iUngroup:
  738.                 DoUngroup(frHndl);
  739.                 break;
  740.         }
  741.     }
  742.     else {
  743.         root = (*frHndl)->d.doc.root;
  744.  
  745.         cnum = 0;
  746.         cdir = 1;
  747.         cend = (*root)->numChildren;
  748.  
  749.         if (cend)
  750.             NewDocumentUndo(frHndl);
  751.  
  752.         if (menuItem >= iMoveBackward) {
  753.             cnum = cend - 1;
  754.             cdir = -1;
  755.             cend = -1;
  756.         }
  757.  
  758.         for (; cnum != cend; cnum += cdir) {
  759.             if (DoTreeObjMethod(GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  760.                 switch (menuItem) {
  761.                     case iMoveForward:
  762.                         if (cnum)
  763.                             MoveChild(MOVEFORWARD_EDIT, root, cnum, root, cnum - 1);
  764.                         break;
  765.                     case iMoveToFront:
  766.                         MoveChild(MOVETOFRONT_EDIT, root, cnum, root, 0);
  767.                         break;
  768.                     case iMoveBackward:
  769.                         MoveChild(MOVEBACKWARD_EDIT, root, cnum, root, cnum + 1);
  770.                         break;
  771.                     case iMoveToBack:
  772.                         MoveChild(MOVETOBACK_EDIT, root, cnum, root, -1);
  773.                         break;
  774.                 }
  775.             }
  776.         }
  777.     }
  778.     SetWindowDirty(window = (*frHndl)->fileState.window);
  779.  
  780.     BeginContent(window);
  781.     DoImageDocument(frHndl);
  782.     EndContent(window);
  783. }
  784.  
  785.  
  786.  
  787. /*****************************************************************************/
  788.  
  789.  
  790.  
  791. #pragma segment DrawDoc
  792. Rect    GetSelectedArea(TreeObjHndl root)
  793. {
  794.     TreeObjHndl    child;
  795.     short        cnum;
  796.     Rect        selectRct, rct;
  797.     Boolean        first;
  798.  
  799.     SetRect(&selectRct, 0, 0, 0, 0);
  800.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  801.         child = GetChildHndl(root, --cnum);
  802.         if (DoTreeObjMethod(child, GETSELECTMESSAGE, 0)) {
  803.             DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  804.             if (!EmptyRect(&rct)) {
  805.                 if (first) {
  806.                     selectRct = rct;
  807.                     first = false;
  808.                 }
  809.                 else UnionRect(&selectRct, &rct, &selectRct);
  810.             }
  811.         }
  812.     }
  813.     return(selectRct);
  814. }    
  815.  
  816.  
  817.  
  818. /*****************************************************************************/
  819.  
  820.  
  821.  
  822. #pragma segment DrawDoc
  823. void    DoGroup(FileRecHndl frHndl)
  824. {
  825.     TreeObjHndl    root, group, child;
  826.     short        gnum, cnum;
  827.     Rect        groupRct;
  828.  
  829.     NewDocumentUndo(frHndl);
  830.  
  831.     root = (*frHndl)->d.doc.root;
  832.     for (gnum = 0; gnum < (*root)->numChildren; ++gnum)
  833.         if (DoTreeObjMethod(GetChildHndl(root, gnum), GETSELECTMESSAGE, 0)) break;
  834.  
  835.     if (!(group = NewChild(GROUP_EDIT, root, gnum, GROUPOBJ, 0))) return;
  836.  
  837.     groupRct = GetSelectedArea(root);
  838.     for (cnum = (*root)->numChildren - 1; cnum > gnum; --cnum) {
  839.         if (DoTreeObjMethod(child = GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  840.             DoTreeObjMethodClipped(child, SETSELECTMESSAGE, SELECTOFF);
  841.             MoveChild(GROUP_EDIT, root, cnum, group, 0);
  842.         }
  843.     }
  844.  
  845.     mDerefGroup(group)->group = groupRct;
  846. }    
  847.  
  848.  
  849.  
  850. /*****************************************************************************/
  851.  
  852.  
  853.  
  854. #pragma segment DrawDoc
  855. void    DoUngroup(FileRecHndl frHndl)
  856. {
  857.     TreeObjHndl    root, group;
  858.     short        gnum, cnum;
  859.  
  860.     NewDocumentUndo(frHndl);
  861.  
  862.     root = (*frHndl)->d.doc.root;
  863.     for (gnum = (*root)->numChildren; gnum;) {
  864.         if (DoTreeObjMethod(group = GetChildHndl(root, --gnum), GETSELECTMESSAGE, 0)) {
  865.             if ((*group)->type == GROUPOBJ) {
  866.                 DoTreeSelect(group, SELECTOFF);
  867.                 for (cnum = (*group)->numChildren; cnum;) {
  868.                     MoveChild(UNGROUP_EDIT, group, --cnum, root, gnum + 1);
  869.                     DoTreeSelect(GetChildHndl(root, gnum + 1), SELECTON);
  870.                 }
  871.                 DisposeChild(UNGROUP_EDIT, root, gnum);
  872.             }
  873.         }
  874.     }
  875. }    
  876.  
  877.  
  878.  
  879. /*****************************************************************************/
  880. /*****************************************************************************/
  881. /*****************************************************************************/
  882.  
  883.  
  884.  
  885. #pragma segment DrawDoc
  886. void    AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click)
  887. {
  888.     TreeObjHndl        root;
  889.     WindowPtr        window;
  890.     short            cnum, h, w;
  891.     LayerObj        windowLayer, workLayer, backLayer;
  892.     Rect            oldRct, newRct, rct;
  893.     OSErr            err;
  894.     LayerDrawInfo    drawInfo;
  895.     Boolean            adding, newTool;
  896.  
  897.     NewDocumentUndo(frHndl);
  898.  
  899.     root   = (*frHndl)->d.doc.root;
  900.     window = (*frHndl)->fileState.window;
  901.  
  902.     adding = newTool = false;
  903.     if (!hndl) {        /* If hndl == nil, then we are adding a new object. */
  904.         adding = true;
  905.         DoTreeSelect(root, SELECTOFF);        /* Turn off all old selections. */
  906.         click->offset.h = 0;
  907.         click->offset.v = 0;
  908.         click->grabber  = 0;
  909.         click->oldFlip  = 0;
  910.         click->newFlip  = 0;
  911.         hndl = NewChild(NEW_EDIT, root, cnum = 0, (RECTOBJ - 1) + gTool, 0);
  912.             /* The child gets created selected, ready to go, except that it's rect is
  913.             ** empty.  As the user drags the object out, this will change. */
  914.  
  915.         if (!gKeepTool) {
  916.             gTool   = 0;
  917.             newTool = true;
  918.         }            /* If the tool is a one-use tool, dispose it. */
  919.  
  920.         if (hndl) {        /* If we succeeded at creating something... */
  921.             rct.top  = rct.bottom = click->localEvent.where.v;
  922.             rct.left = rct.right  = click->localEvent.where.h;
  923.             DoTreeObjMethod(hndl, SETBBOXMESSAGE, (long)&rct);
  924.             DoTreeObjMethod(hndl, UNDOMESSAGE, UNDOFROMDOC);
  925.                 /* This is a safe (non-drawing) way to deselect the object.
  926.                 ** Just forcing the select field to false does not update
  927.                 ** the numSelected field in the root object.  We have to
  928.                 ** account for the deselection. */
  929.         }
  930.     }
  931.     else {
  932.         cnum = GetChildNum(hndl);
  933.         if (ModifyChild(SIZE_EDIT, root, cnum, false)) return;
  934.             /* Out of memory.  I would handle this case better if it weren't just sample code. */
  935.     }
  936.  
  937.     drawInfo.root = root;        /* The tree we are operating with. */
  938.     drawInfo.cnum = cnum;        /* The backmost object that is changing. */
  939.  
  940.     if (hndl) {
  941.         NewLayer(&windowLayer, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  942.         err = NewLayer(&workLayer, windowLayer, WorkLayerProc, nil, 0, (long)&drawInfo);
  943.         if (!err)
  944.             err = NewLayer(&backLayer, workLayer, BackLayerProc, nil, 0, (long)&drawInfo);
  945.         if (err) DisposeThisAndBelowLayers(workLayer);
  946.             /* The above code creates the necessary offscreen layers for the following editing.
  947.             ** The windowLayer is bound to succeed at getting created, as it uses the window,
  948.             ** instead of creating an offscreen GWorld.  The workLayer and backLayer creations
  949.             ** may fail.  If they do, then we will only have a windowLayer.  The windowLayer
  950.             ** is smart enough to notice that it is alone, and if it is, then it will do the
  951.             ** drawing directly to the window.  Drawing directly to the window means that there
  952.             ** will be a bunch of flicker, but what else is there to do when there is not enough
  953.             ** ram?  Drawing nothing is even worse. */
  954.  
  955.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  956.             /* Bounding rect of the object getting created or resized. */
  957.  
  958.         if (click->grabber)
  959.             InsetRect(&rct, -3, -3);
  960.         InvalLayer(windowLayer, rct, true);
  961.             /* If getting resized, then the object has grabbers, and we have to make sure
  962.             ** that the area getting redrawn is large enough for the grabbers. */
  963.  
  964.         while (StillDown()) {
  965.             DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&oldRct);
  966.             if (DoTreeObjMethod(hndl, SIZEMESSAGE, (long)click)) {        /* If new size... */
  967.                 DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&newRct);
  968.                 if (!err) {        /* If we have all offscreen layers... */
  969.                     if (click->grabber)
  970.                         InsetRect(&newRct, -3, -3);
  971.                     InvalLayer(windowLayer, newRct, true);
  972.                     UpdateLayer(windowLayer);
  973.                 }
  974.                 else {            /* If we only have windowLayer... */
  975.                     DoTreeObjMethod(hndl, SETBBOXMESSAGE, (long)&oldRct);
  976.                     DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  977.                     DoTreeObjMethod(hndl, SETBBOXMESSAGE, (long)&newRct);
  978.                     DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  979.                 }
  980.             }
  981.         }
  982.  
  983.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  984.         h = rct.bottom - rct.top;
  985.         w = rct.right  - rct.left;
  986.             /* This is how big the final product is.  It may now be unacceptably small.
  987.             ** If we were adding a new object and it is too small, get rid of it entirely
  988.             ** and use this to deselect the tool.  If we were resizing an existing object,
  989.             ** then force the object to be a minimum size. */
  990.  
  991.         if (((!h) || (!w)) || ((h < 8) && (w < 8))) {    /* If smaller than minimum... */
  992.             if (adding) {
  993.                 RevertEdit(hndl);        /* Don't create a new object that's too small. */
  994.                 hndl = nil;                /* We didn't change anything, so flag this situation. */
  995.                 gTool     = 0;
  996.                 gKeepTool = false;
  997.                 newTool   = true;        /* Show that we are done with the tool. */
  998.             }
  999.             else {
  1000.                 rct.bottom = rct.top  + 8;                /* Don't let them shrink it too small. */
  1001.                 rct.right  = rct.left + 8;
  1002.                 DoTreeObjMethod(hndl, SETBBOXMESSAGE, (long)&rct);    /* Make it minimum. */
  1003.             }
  1004.             DoImageDocument(frHndl);
  1005.         }
  1006.  
  1007.         if (hndl) {        /* If we added or changed something... */
  1008.             SetWindowDirty(window);
  1009.             if (err)
  1010.                 DoTreeObjMethod(hndl, DRAWMESSAGE, DRAWOBJ);
  1011.                     /* If we have only a windowLayer, then we haven't really been generating a
  1012.                     ** complete image.  Redraw the document so that a full image is shown. */
  1013.             DoTreeSelect(hndl, SELECTON);
  1014.                 /* Make sure the object is selected. */
  1015.         }
  1016.  
  1017.         DisposeThisAndBelowLayers(windowLayer);        /* Clean up. */
  1018.     }
  1019.  
  1020.     if (newTool)
  1021.         SetPaletteTool(gTool, gKeepTool);
  1022. }
  1023.  
  1024.  
  1025.  
  1026. /*****************************************************************************/
  1027.  
  1028.  
  1029.  
  1030. #pragma segment DrawDoc
  1031. static OSErr    WindowLayerProc(LayerObj theLayer, short message)
  1032. {
  1033.     OSErr            err;
  1034.     WindowPtr        oldPort, window;
  1035.     Point            contOrg;
  1036.     Rect            contRct, thisUpdate;
  1037.     LayerDrawInfo    drawInfo;
  1038.     RgnHandle        oldClip, newClip;
  1039.  
  1040.     err = noErr;
  1041.  
  1042.     drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1043.  
  1044.     switch (message) {
  1045.         case kLayerInit:
  1046.             err = DefaultLayerProc(theLayer, message);
  1047.             GetPort(&oldPort);
  1048.             SetPort(window = (*theLayer)->layerPort);
  1049.             GetContentOrigin(window, &contOrg);
  1050.             SetOrigin(contOrg.h, contOrg.v);
  1051.             GetContentRect(window, &contRct);
  1052.             (*theLayer)->dstRect = contRct;
  1053.                 /* The above calculates the content rect (less tool palette and scrollbars).
  1054.                 ** By setting the dstRect to this smaller rect, workLayer and backLayer
  1055.                 ** will be created the size of dstRect, instead of the portRect of the
  1056.                 ** window.  This in turn will keep the GWLayers code from drawing over
  1057.                 ** the tool palette and scrollbars. */
  1058.             SetPort(oldPort);
  1059.             break;
  1060.         case kLayerUpdate:
  1061.             BeginContent((*theLayer)->layerPort);
  1062.             if ((*theLayer)->belowLayer)
  1063.                 DefaultLayerProc(theLayer, message);
  1064.             else {        /* If offscreen layers couldn't be made, then do it by hand. */
  1065.                 thisUpdate = UpdateUpdateRects(theLayer);
  1066.                 oldClip = NewRgn();
  1067.                 newClip = NewRgn();
  1068.                 RectRgn(newClip, &thisUpdate);
  1069.                 GetClip(oldClip);
  1070.                 SetClip(newClip);
  1071.                 EraseRect(&thisUpdate);
  1072.                 DoTreeDraw(drawInfo.root, DRAWOBJ);
  1073.                 DoTreeDraw(drawInfo.root, DRAWSELECT);
  1074.                 SetClip(oldClip);
  1075.                 DisposeRgn(oldClip);
  1076.                 DisposeRgn(newClip);
  1077.             }
  1078.             EndContent((*theLayer)->layerPort);
  1079.             break;
  1080.  
  1081.         default:
  1082.             err = DefaultLayerProc(theLayer, message);
  1083.                 /* Default behavior for everything else. */
  1084.             break;
  1085.     }
  1086.  
  1087.     return(err);
  1088. }
  1089.  
  1090.  
  1091.  
  1092. /*****************************************************************************/
  1093.  
  1094.  
  1095.  
  1096. #pragma segment DrawDoc
  1097. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1098. {
  1099.     OSErr            err;
  1100.     Rect            rct;
  1101.     LayerDrawInfo    drawInfo;
  1102.     short            cnum;
  1103.     TreeObjHndl        chndl;
  1104.  
  1105.     err = noErr;
  1106.  
  1107.     switch (message) {
  1108.         case kLayerInit:
  1109.             err = DefaultLayerProc(theLayer, message);
  1110.             break;
  1111.         case kLayerUpdate:
  1112.             SetLayerWorld(theLayer);
  1113.             drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1114.             if ((*theLayer)->belowLayer)
  1115.                 DefaultLayerProc(theLayer, message);
  1116.             else {
  1117.                 rct = GetEffectiveDstRect(theLayer);
  1118.                 EraseRect(&rct);
  1119.                 drawInfo.cnum = (*drawInfo.root)->numChildren - 1;
  1120.             }
  1121.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1122.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1123.                 DoTreeDraw(chndl, DRAWOBJ);
  1124.             }
  1125.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1126.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1127.                 DoTreeDraw(chndl, DRAWSELECT);
  1128.             }
  1129.             ResetLayerWorld(theLayer);
  1130.             break;
  1131.         default:
  1132.             err = DefaultLayerProc(theLayer, message);
  1133.                 /* Default behavior for everything else. */
  1134.             break;
  1135.     }
  1136.  
  1137.     return(err);
  1138. }
  1139.  
  1140.  
  1141.  
  1142. /*****************************************************************************/
  1143.  
  1144.  
  1145.  
  1146. #pragma segment DrawDoc
  1147. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1148. {
  1149.     OSErr            err;
  1150.     Rect            rct;
  1151.     LayerDrawInfo    drawInfo;
  1152.     short            cnum;
  1153.     TreeObjHndl        chndl;
  1154.  
  1155.     err = noErr;
  1156.  
  1157.     switch (message) {
  1158.         case kLayerInit:
  1159.             err = DefaultLayerProc(theLayer, message);
  1160.             if (!err) {
  1161.                 SetLayerWorld(theLayer);
  1162.                 rct = GetEffectiveDstRect(theLayer);
  1163.                 EraseRect(&rct);
  1164.                 drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1165.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1166.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1167.                     DoTreeDraw(chndl, DRAWOBJ);
  1168.                 }
  1169.                 ResetLayerWorld(theLayer);
  1170.             }
  1171.             break;
  1172.         default:
  1173.             err = DefaultLayerProc(theLayer, message);
  1174.                 /* Default behavior for everything else. */
  1175.             break;
  1176.     }
  1177.  
  1178.     return(err);
  1179. }
  1180.  
  1181.  
  1182.  
  1183. /*****************************************************************************/
  1184.  
  1185.  
  1186.  
  1187. #pragma segment DrawDoc
  1188. void    SlideSelection(FileRecHndl frHndl, ClickInfo *click)
  1189. {
  1190.     LayerDrawInfo    drawInfo;
  1191.     WindowPtr        window;
  1192.     LayerObj        windowLayer, workLayer, backLayer;
  1193.     Point            lastMouse, curMouse;
  1194.     short            cnum;
  1195.     TreeObjHndl        root, chndl;
  1196.     Rect            newLoc, selectRct;
  1197.     Boolean            changed;
  1198.  
  1199.     changed = false;
  1200.     NewDocumentUndo(frHndl);
  1201.  
  1202.     root = (*frHndl)->d.doc.root;
  1203.     for (cnum = (*root)->numChildren;;) {
  1204.         if (DoTreeObjMethod(GetChildHndl(root, --cnum), GETSELECTMESSAGE, 0)) break;
  1205.         if (!cnum) return;
  1206.     }
  1207.  
  1208.     drawInfo.root = root;
  1209.     drawInfo.cnum = cnum;
  1210.     window        = (*frHndl)->fileState.window;
  1211.  
  1212.     NewLayer(&windowLayer, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1213.     NewLayer(&workLayer, windowLayer, WorkLayerProc, nil, 0, (long)&drawInfo);
  1214.     NewLayer(&backLayer, workLayer, BackLayerProc, nil, 0, (long)&drawInfo);
  1215.  
  1216.     selectRct = GetSelectedArea(root);
  1217.     InsetRect(&selectRct, -3, -3);
  1218.     InvalLayer(windowLayer, selectRct, false);
  1219.     lastMouse = click->localEvent.where;
  1220.     while (StillDown()) {
  1221.         GetMouse(&curMouse);
  1222.         click->offset.h = (curMouse.h - lastMouse.h);
  1223.         click->offset.v = (curMouse.v - lastMouse.v);
  1224.         click->message  = CLICKDRAG;
  1225.         if ((click->offset.h) || (click->offset.v)) {
  1226.             changed = true;
  1227.             for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  1228.                 chndl = GetChildHndl(root, cnum);
  1229.                 if (mDerefCommon(chndl)->selected) {
  1230.                     ModifyChild(MOVE_EDIT, root, cnum, true);
  1231.                     DoFTreeMethod(chndl, CLICKMESSAGE, (long)click);
  1232.                     DoTreeObjMethod(chndl, GETBBOXMESSAGE, (long)&newLoc);
  1233.                     InsetRect(&newLoc, -3, -3);
  1234.                     InvalLayer(windowLayer, newLoc, true);
  1235.                 }
  1236.             }
  1237.             UpdateLayer(windowLayer);
  1238.             lastMouse = curMouse;
  1239.         }
  1240.     }
  1241.  
  1242.     if (changed)
  1243.         SetWindowDirty(window);
  1244.     DisposeThisAndBelowLayers(windowLayer);
  1245. }
  1246.  
  1247.  
  1248.  
  1249. /*****************************************************************************/
  1250.  
  1251.  
  1252.  
  1253. #pragma segment DrawDoc
  1254. void    NewDocumentUndo(FileRecHndl frHndl)
  1255. {
  1256.     NewUndo((*frHndl)->d.doc.root);
  1257. }
  1258.  
  1259.  
  1260.  
  1261.